c->sysenter_esp = vmcb->sysenter_esp;
c->sysenter_eip = vmcb->sysenter_eip;
+ /* Save any event/interrupt that was being injected when we last
+ * exited. Although there are three(!) VMCB fields that can contain
+ * active events, we only need to save at most one: because the
+ * intr_assist logic never delivers an IRQ when any other event is
+ * active, we know that the only possible collision is if we inject
+ * a fault while exitintinfo contains a valid event (the delivery of
+ * which caused the last exit). In that case replaying just the
+ * first event should cause the same behaviour when we restore. */
+ if ( vmcb->vintr.fields.irq
+ && /* Check it's not a fake interrupt (see svm_intr_assist()) */
+ !(vmcb->general1_intercepts & GENERAL1_INTERCEPT_VINTR) )
+ {
+ c->pending_vector = vmcb->vintr.fields.vector;
+ c->pending_type = 0; /* External interrupt */
+ c->pending_error_valid = 0;
+ c->pending_reserved = 0;
+ c->pending_valid = 1;
+ c->error_code = 0;
+ }
+ else if ( vmcb->exitintinfo.fields.v )
+ {
+ c->pending_event = vmcb->exitintinfo.bytes & 0xffffffff;
+ c->error_code = vmcb->exitintinfo.fields.errorcode;
+ }
+ else if ( vmcb->eventinj.fields.v )
+ {
+ c->pending_event = vmcb->eventinj.bytes & 0xffffffff;
+ c->error_code = vmcb->eventinj.fields.errorcode;
+ }
+ else
+ {
+ c->pending_event = 0;
+ c->error_code = 0;
+ }
+
return 1;
}
if ( !svm_paging_enabled(v) )
{
- printk("%s: paging not enabled.", __func__);
+ printk("%s: paging not enabled.\n", __func__);
goto skip_cr3;
}
vmcb->dr6 = c->dr6;
vmcb->dr7 = c->dr7;
+ if ( c->pending_valid )
+ {
+ gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n",
+ c->pending_event, c->error_code);
+
+ /* VMX uses a different type for #OF and #BP; fold into "Exception" */
+ if ( c->pending_type == 6 )
+ c->pending_type = 3;
+ /* Sanity check */
+ if ( c->pending_type == 1 || c->pending_type > 4
+ || c->pending_reserved != 0 )
+ {
+ gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32"\n",
+ c->pending_event);
+ return -EINVAL;
+ }
+ /* Put this pending event in exitintinfo and svm_intr_assist()
+ * will reinject it when we return to the guest. */
+ vmcb->exitintinfo.bytes = c->pending_event;
+ vmcb->exitintinfo.fields.errorcode = c->error_code;
+ }
+
paging_update_paging_modes(v);
return 0;
bad_cr3:
- gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%"PRIx64"", c->cr3);
+ gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%"PRIx64"\n", c->cr3);
return -EINVAL;
}
int vmx_vmcs_save(struct vcpu *v, struct hvm_hw_cpu *c)
{
+ uint32_t ev;
+
c->rip = __vmread(GUEST_RIP);
c->rsp = __vmread(GUEST_RSP);
c->rflags = __vmread(GUEST_RFLAGS);
c->sysenter_esp = __vmread(GUEST_SYSENTER_ESP);
c->sysenter_eip = __vmread(GUEST_SYSENTER_EIP);
+ /* Save any event/interrupt that was being injected when we last
+ * exited. IDT_VECTORING_INFO_FIELD has priority, as anything in
+ * VM_ENTRY_INTR_INFO_FIELD is either a fault caused by the first
+ * event, which will happen the next time, or an interrupt, which we
+ * never inject when IDT_VECTORING_INFO_FIELD is valid.*/
+ if ( (ev = __vmread(IDT_VECTORING_INFO_FIELD)) & INTR_INFO_VALID_MASK )
+ {
+ c->pending_event = ev;
+ c->error_code = __vmread(IDT_VECTORING_ERROR_CODE);
+ }
+ else if ( (ev = __vmread(VM_ENTRY_INTR_INFO_FIELD))
+ & INTR_INFO_VALID_MASK )
+ {
+ c->pending_event = ev;
+ c->error_code = __vmread(VM_ENTRY_EXCEPTION_ERROR_CODE);
+ }
+ else
+ {
+ c->pending_event = 0;
+ c->error_code = 0;
+ }
+
return 1;
}
vmx_vmcs_exit(v);
paging_update_paging_modes(v);
+
+ if ( c->pending_valid )
+ {
+ vmx_vmcs_enter(v);
+ gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n",
+ c->pending_event, c->error_code);
+
+ /* SVM uses type 3 ("Exception") for #OF and #BP; VMX uses type 6 */
+ if ( c->pending_type == 3
+ && (c->pending_vector == 3 || c->pending_vector == 4) )
+ c->pending_type = 6;
+
+ /* For software exceptions, we need to tell the hardware the
+ * instruction length as well (hmmm). */
+ if ( c->pending_type > 4 )
+ {
+ int addrbytes, ilen;
+ if ( (c->cs_arbytes & (1u<<13)) && (c->msr_efer & EFER_LMA) )
+ addrbytes = 8;
+ else if ( (c->cs_arbytes & (1u<<14)) )
+ addrbytes = 4;
+ else
+ addrbytes = 2;
+ ilen = hvm_instruction_length(c->rip, hvm_guest_x86_mode(v));
+ __vmwrite(VM_ENTRY_INSTRUCTION_LEN, ilen);
+ }
+
+ /* Sanity check */
+ if ( c->pending_type == 1 || c->pending_type > 6
+ || c->pending_reserved != 0 )
+ {
+ gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32"\n",
+ c->pending_event);
+ return -EINVAL;
+ }
+ /* Re-inject the exception */
+ __vmwrite(VM_ENTRY_INTR_INFO_FIELD, c->pending_event);
+ __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, c->error_code);
+ v->arch.hvm_vmx.vector_injected = 1;
+ vmx_vmcs_exit(v);
+ }
+
return 0;
bad_cr3:
/* guest's idea of what rdtsc() would return */
uint64_t tsc;
+
+ /* pending event, if any */
+ union {
+ uint32_t pending_event;
+ struct {
+ uint8_t pending_vector:8;
+ uint8_t pending_type:3;
+ uint8_t pending_error_valid:1;
+ uint32_t pending_reserved:19;
+ uint8_t pending_valid:1;
+ };
+ };
+ /* error code for pending event */
+ uint32_t error_code;
};
DECLARE_HVM_SAVE_TYPE(CPU, 2, struct hvm_hw_cpu);